#ifndef __M_SESSION_H__
#define __M_SESSION_H__

#pragma once

#include "online.hpp"

#define SESSION_FOREVER -1
#define SESSION_TIMEOUT 30000

enum session_status
{
    LOGOUT,
    LOGIN
};

class session
{
public:
    session(uint64_t sid)
    : _sid(sid)
    {
        LOG(DEG, "session %p create success", this);
    }

    ~session()
    {
        LOG(DEG, "session %p destroy", this);
    }

    uint64_t get_sid() { return _sid; }
    void set_user(uint64_t uid) { _uid = uid; }
    uint64_t get_user() { return _uid; }
    void set_status(session_status st) { _st = st; }
    session_status get_status() { return _st; }
    bool is_login() { return _st == LOGIN; }
    void set_timer(const wsserver_t::timer_ptr& tp) { _tp = tp; }
    wsserver_t::timer_ptr& get_timer() { return _tp; }
private:
    uint64_t _sid;
    uint64_t _uid;
    session_status _st;
    wsserver_t::timer_ptr _tp;
};

using session_ptr = std::shared_ptr<session>;

class session_manager
{
public:
    session_manager(wsserver_t* server)
    : _next_sid(1),
      _server(server)
    {
        LOG(DEG, "session_manager create");
    }

    ~session_manager()
    {
        LOG(DEG, "session_manager destroy");
    }

    session_ptr create_session(uint64_t uid, session_status st)
    {
        std::unique_lock<std::mutex> lock(_mutex);//保护_next_sid和map增删查改的安全
        session_ptr sp(new session(_next_sid));
        sp->set_user(uid);
        sp->set_status(st);
        _sessions.insert(std::make_pair(_next_sid, sp));
        _next_sid++;
        return sp;
    }

    session_ptr get_session(uint64_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _sessions.find(sid);
        if (it == _sessions.end())
            return session_ptr();
        return it->second;
    }

    void remove_session(uint64_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _sessions.find(sid);
        if (it == _sessions.end())
            return;
        _sessions.erase(sid);
    }

    void append_session(session_ptr sp)//重新添加session信息
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _sessions.insert(std::make_pair(sp->get_sid(), sp));
    }

    void set_expire_time(uint64_t sid, int ms)
    {
        session_ptr sp = get_session(sid);//注意这里引用计数+1 即使调用了remove_session接口 session信息也不会被删除
        if (sp.get() == nullptr)
            return;
        
        uint64_t uid = sp->get_user();
        session_status st = sp->get_status();
        wsserver_t::timer_ptr tp = sp->get_timer();
        if (tp.get() == nullptr && ms == SESSION_FOREVER)//session永久存在的情况下 设置永久存在
        {
            return;
        }
        else if (tp.get() == nullptr && ms != SESSION_FOREVER)//session永久存在的情况下 设置定时删除
        {
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, sid));
            sp->set_timer(tmp_tp);
        }
        else if (tp.get() != nullptr && ms == SESSION_FOREVER)//session设置定时删除的情况下 设置永久存在
        {
            tp->cancel();//取消定时任务会立即执行任务 但并不是严格意义上的立即执行 类似与异步微任务? 需要防止先添加后删除导致没有添加的情况
            sp->set_timer(wsserver_t::timer_ptr());//重置session信息中的定时器
            //session信息节点从哈希结构中删除后 需要将先前拷贝构造的session_ptr插入到哈希中 此时引用计数为2 当前函数执行完毕后 引用计数为1
            _server->set_timer(0, std::bind(&session_manager::append_session, this, sp));//重新插入session信息 不能直接添加而需要通过定时器添加
        }
        else if (tp.get() != nullptr && ms != SESSION_FOREVER)//session设置定时删除的情况下 重置删除时间
        {
            tp->cancel();
            sp->set_timer(wsserver_t::timer_ptr());//这里其实不用重置定时器 因为定时删除任务已被执行且后续会重新添加定时删除任务
            _server->set_timer(0, std::bind(&session_manager::append_session, this, sp));
            
            wsserver_t::timer_ptr tmp_tp = _server->set_timer(ms, std::bind(&session_manager::remove_session, this, sid));
            sp->set_timer(tmp_tp);//此时引用计数为2 sp和哈希中插入的节点指向的session信息是同一块空间 通过sp修改session信息和通过节点修改具有相同效果
        }
        else
        {
            LOG(DEG, "unknown ms type");
        }
    }

private:
    uint64_t _next_sid;
    std::mutex _mutex;
    std::unordered_map<uint64_t, session_ptr> _sessions;
    wsserver_t* _server;
};

#endif